package srzy.common

import scala.Array.canBuildFrom
import scala.annotation.tailrec

object packunpack extends Logging {

  def hexToInt(c: Char): Int = c match {
    case '0' => 0
    case '1' => 1
    case '2' => 2
    case '3' => 3
    case '4' => 4
    case '5' => 5
    case '6' => 6
    case '7' => 7
    case '8' => 8
    case '9' => 9
    case 'a' | 'A' => 10
    case 'b' | 'B' => 11
    case 'c' | 'C' => 12
    case 'd' | 'D' => 13
    case 'e' | 'E' => 14
    case 'f' | 'F' => 15
    case _ => 0
  }

  def leftPad(str: String, i: Int, pad: Char = '0') = (List.fill(i)(pad).mkString("") + str).substring(str.length())

  def pack(arr: Array[Byte], offset: Int, value: Byte) =
    arr.update(offset, value)

  def packInt(arr: Array[Byte], offset: Int, value: Int, digit: Int = 4): Array[Byte] = {
    if (digit > 4 || digit < 1) throw new Exception("digit " + digit + " illegal")
    var i = 0
    while (i < digit) {
      //      println("i:" + i + "," + (8 * (digit - i - 1)) + "," + byteToHexString(value.toLong.>>>((8 * (digit - i - 1))).asInstanceOf[Byte]))
      arr.update(offset + i, (value.>>>((8 * (digit - i - 1))) & 0xFF).asInstanceOf[Byte])
      i += 1
    }
    arr
  }

  def packLong(arr: Array[Byte], offset: Int, value: Long, digit: Int = 8): Array[Byte] = {
    if (digit > 8 || digit < 1) throw new Exception("digit " + digit + " illegal")
    var i = 0
    while (i < digit) {
      //      println("i:" + i + "," + (8 * (digit - i - 1)) + "," + byteToHexString(value.toLong.>>>((8 * (digit - i - 1))).asInstanceOf[Byte]))
      arr.update(offset + i, (value.>>>((8 * (digit - i - 1))) & 0xFF).asInstanceOf[Byte])
      i += 1
    }
    arr
  }

  def packFloat(arr: Array[Byte], offset: Int, f: Float): Array[Byte] = {
    val intBits = java.lang.Float.floatToIntBits(f)
    packInt(arr, offset, intBits)
  }

  def packDouble(arr: Array[Byte], offset: Int, d: Double): Array[Byte] = {
    val longBits = java.lang.Double.doubleToLongBits(d)
    packLong(arr, offset, longBits)
  }

  //  def unpackInt(arr: Array[Byte], offset: Int, digit: Int = 4): Int = {
  //    if (digit > 4 || digit < 1) throw new Exception("digit " + digit + " illegal")
  //
  //    @tailrec def unpackN(v: Int, n: Int): Int =
  //      if (n == 0) {
  //        v | (arr.apply(offset).&(0xFF).<<((digit - 1) * 8))
  //      } else {
  //        //        println((arr.apply(offset + n).&(0xFF).toLong.<<((digit - 1 - n) * 8)))
  //        unpackN(v | (arr.apply(offset + n).&(0xFF).<<((digit - 1 - n) * 8)), n - 1)
  //      }
  //    unpackN(0, digit - 1)
  //  }

  def unpackInt(arr: Array[Byte], offset: Int, digit: Int = 4): Int = {
    if (digit > 4 || digit < 1) throw new Exception("digit " + digit + " illegal")
    val s = byteToHexString(arr).slice(offset * 2, offset * 2 + digit * 2)
    //    println("s:" + s + ",offset:" + offset + ",digit:" + digit)
    var v: Int = 0
    val len = s.length
    for (i <- (0 to (len - 1)).reverse) {
      v = v + (hexToInt(s.apply(i)) * scala.math.pow(16, len - i - 1)).toInt
    }
    v
  }

  //  def unpackLong(arr: Array[Byte], offset: Int, digit: Int = 8): Long = {
  //    if (digit > 8 || digit < 1) throw new Exception("digit " + digit + " illegal")
  //
  //    @tailrec def unpackN(v: Long, n: Int): Long =
  //      if (n == 0) {
  //        v | (arr.apply(offset).&(0xFF).toLong.<<((digit - 1) * 8))
  //      } else {
  //        println(arr)
  //        println(arr.apply(offset + n))
  //        println((arr.apply(offset + n).&(0xFF).toLong.<<((digit - 1 - n) * 8)))
  //        unpackN(v | (arr.apply(offset + n).&(0xFF).toLong.<<((digit - 1 - n) * 8)), n - 1)
  //      }
  //    unpackN(0, digit - 1)
  //  }

  def unpackLong(arr: Array[Byte], offset: Int, digit: Int = 8): Long = {
    if (digit > 8 || digit < 1) throw new Exception("digit " + digit + " illegal")
    val s = byteToHexString(arr).slice(offset * 2, offset * 2 + digit * 2)
    //    println("s:" + s)
    //    println("s:" + s + ",offset:" + offset + ",digit:" + digit)

    var v: Long = 0
    val len = s.length
    for (i <- (0 to (len - 1)).reverse) {
      v = v + (hexToInt(s.apply(i)).toLong * scala.math.pow(16, len - i - 1)).toLong
    }
    v
  }

  def unpackFloat(arr: Array[Byte], offset: Int): Float = {
    val intBits = unpackInt(arr, offset)
    java.lang.Float.intBitsToFloat(intBits)
  }

  def unpackDouble(arr: Array[Byte], offset: Int): Double = {
    val longBits = unpackLong(arr, offset)
    java.lang.Double.longBitsToDouble(longBits)
  }

  def byteToHexString(b: Array[Byte]): String =
    b.toList.map(x => byteToHexString(x)).mkString("")

  def byteToHexString(b: Byte): String = leftPad(Integer.toHexString(b & 0xff), 2)

  def hexStr2Bytes(hexStr: String) = {
    if (hexStr.length() % 2 != 0) throw new Exception("hex string is not even")
    val str = "0123456789abcdef"
    val hexs = hexStr.toLowerCase().toCharArray()
    val bytes = Array.fill(hexStr.length() / 2)('0')
    bytes.zipWithIndex.map(x => {
      val c = x._1
      val i = x._2
      val n = str.indexOf(hexs.apply(2 * i)) * 16 + str.indexOf(hexs.apply(2 * i + 1))
      (n & 0xff).asInstanceOf[Byte]
    })
  }

}